#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Log.hpp"

extern Log lg;

template <class T>
class BlockQueue
{
    static const int defaultcap = 10;
public:
    BlockQueue(int maxcap = defaultcap)
        :maxcap_(maxcap)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&c_cond_, nullptr);
        pthread_cond_init(&p_cond_, nullptr);
    }

    void push(const T &in)
    {
        pthread_mutex_lock(&mutex_);

        // 为了防止出现错误唤醒的情况
        // 将 if 替换成 while
        while (q_.size() == maxcap_)
        {
            // 阻塞队列资源满了，将生产者加入等待队列
            pthread_cond_wait(&p_cond_, &mutex_);
        }
        // 能生产数据的前提: 1. 队列未满 2. 被唤醒
        q_.push(in);

        // 高于水位线，唤醒消费者
        if (q_.size() > high_water) 
        {
            pthread_cond_signal(&c_cond_);
        }

        pthread_mutex_unlock(&mutex_);
    }

    const T pop()
    {
        pthread_mutex_lock(&mutex_);

        while (q_.size() == 0)
        {
            pthread_cond_wait(&c_cond_, &mutex_);
        }
        // 能消费数据的前提: 1. 队列有数据 2. 被唤醒
        T out = q_.front();
        q_.pop();

        if (q_.size() < low_water)
        {
            pthread_cond_signal(&p_cond_);
        }

        pthread_mutex_unlock(&mutex_);

        return out;
    }    
    
    int GetSize()
    {
        return q_.size();
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond_);
        pthread_cond_destroy(&p_cond_);
    }
private:
    std::queue<T> q_;                  //共享资源
    int maxcap_;                       //极值
    pthread_mutex_t mutex_;
    pthread_cond_t c_cond_;            // 唤醒消费者的条件变量
    pthread_cond_t p_cond_;            // 唤醒生产者的条件变量

    int low_water  = defaultcap / 3;   // 最低水位
    int high_water = defaultcap*2 / 3; // 最高水位
};